package cn.shengchao.examstar.pay.model.service.fy.dto;

import java.util.*;
import java.util.stream.Collectors;

public class ParameterCollection implements Iterable<Parameter> {
    private List<Parameter> parameters = new ArrayList<>();

    public void add(String name, String value) {
        if (name != null) {
            this.parameters.add(new Parameter(name, value));
        }
    }

    public void add(Parameter parameter) {
        if (parameter != null && parameter.getName() != null) {
            // Optional: check for duplicates or decide replacement strategy if needed
            this.parameters.add(parameter);
        }
    }

    public String getValue(String name) {
        return parameters.stream()
                .filter(p -> p.getName().equals(name))
                .map(Parameter::getValue)
                .findFirst()
                .orElse(null);
    }

    public List<Parameter> getParameters() {
        // Return a defensive copy to prevent external modification
        return new ArrayList<>(parameters);
    }

    public ParameterCollection filterForSigning() {
        ParameterCollection filtered = new ParameterCollection();
        for (Parameter p : this.parameters) {
            String key = p.getName();
            if ("sign".equalsIgnoreCase(key)
                    || (key != null && key.length() >= 8 && "reserved".equalsIgnoreCase(key.substring(0, 8)))) {
                continue;
            }
            // Ensure that even if a value is null, the parameter itself is added if it's not filtered out
            filtered.add(p.getName(), p.getValue());
        }
        return filtered;
    }

    public String createLinkString() {
        List<Parameter> sortedParameters = new ArrayList<>();
        for (Parameter p : this.parameters) {
            // FuYou typically requires empty strings for null values in the signature string
            // and often filters out parameters with null values (depending on their spec for specific fields).
            // The original map.get(key) would lead to "key=null" if value was null string, or NPE if map contained actual null.
            // Let's ensure behavior is "key=" for null value, and filter out params if value is truly null.
            // However, some APIs might want "key=" for nulls. The current filterForSigning keeps them.
            if (p.getValue() != null) { // Only include parameters with non-null values for linking
                sortedParameters.add(new Parameter(p.getName(), p.getValue()));
            } else { // Or include parameters with null values as "key="
                sortedParameters.add(new Parameter(p.getName(), ""));
            }
        }

        // Sort by key name AFTER filtering and value handling
        sortedParameters.sort(Comparator.comparing(Parameter::getName));

        StringBuilder prestr = new StringBuilder();
        for (int i = 0; i < sortedParameters.size(); i++) {
            Parameter p = sortedParameters.get(i);
            prestr.append(p.getName()).append("=").append(p.getValue()); // Value is already "" if it was null
            if (i < sortedParameters.size() - 1) {
                prestr.append("&");
            }
        }
        return prestr.toString();
    }

    @Override
    public Iterator<Parameter> iterator() {
        return Collections.unmodifiableList(new ArrayList<>(parameters)).iterator();
    }

    public boolean isEmpty() {
        return parameters.isEmpty();
    }

    public int size() {
        return parameters.size();
    }

    public static ParameterCollection fromMap(Map<String, String> map) {
        ParameterCollection pc = new ParameterCollection();
        if (map != null) {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                pc.add(entry.getKey(), entry.getValue());
            }
        }
        return pc;
    }

    public Map<String, String> toMap() {
        return parameters.stream()
                .collect(Collectors.toMap(Parameter::getName,
                        p -> p.getValue() == null ? "" : p.getValue(),
                        (v1, v2) -> v2)); // Prioritize last entry for duplicates
    }
}
